package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.gson.Gson;
import com.itheima.stock.common.domian.*;
import com.itheima.stock.common.enums.ResponseCode;
import com.itheima.stock.config.StockInfoConfig;
import com.itheima.stock.mapper.StockBlockRtInfoMapper;
import com.itheima.stock.mapper.StockBusinessMapper;
import com.itheima.stock.mapper.StockMarketIndexInfoMapper;
import com.itheima.stock.mapper.StockRtInfoMapper;
import com.itheima.stock.pojo.StockBusiness;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author by itheima
 * @Date 2021/12/19
 * @Description
 */
@Service
public class StockServiceImpl implements StockService {

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Override
    public List<StockBusiness> getAllStockBusiness() {
        return stockBusinessMapper.getAll();
    }

    @Override
    public R<List<InnerMarketDomain>> innerIndexAll() {
        // 获取国内大盘id的集合
        List<String> inner = stockInfoConfig.getInner();
        // 获取最近更新的有效股票交易日
        DateTime lastDate4Stock = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date date = lastDate4Stock.toDate();
        // TODO moke测试数据
        String mokeDate = "20211226105600";
        date = DateTime.parse(mokeDate, DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();
        // 执行查询
        List<InnerMarketDomain> marketDomains = stockMarketIndexInfoMapper.innerIndexAll(inner, date);


        return R.ok(marketDomains);
    }

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Override
    public R<List<StockBlockDomain>> sectorAllLimit() {
        // 调用mapper接口查询最新板块数据， 根据最新时间查询 避免全表查询
        List<StockBlockDomain> blockRtInfoList = stockBlockRtInfoMapper.stockBlockAll();
        // 检查参数
        if(CollectionUtils.isEmpty(blockRtInfoList)) {
            R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(blockRtInfoList);
    }

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    /**
     * 统计沪深两市个股最新数据，并按涨幅降序排序
     * 并查询前10条内容
     * @return
     */
    @Override
    public R<List<StockUpdownDomain>> getStockRtInfo() {
        // 最去最近更新的有效股票交易日
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        // TODO moke 测试假数据
        String mokeDate = "2021-12-30 09:32:00";
        lastDate = DateTime.parse(mokeDate, DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        // 查询数据库
        List<StockUpdownDomain> updownDomainLIst = stockRtInfoMapper.getStockRtInfo(lastDate);
        return R.ok(updownDomainLIst);
    }

    /**
     * 沪深两市个股行情列表查询 ,以时间顺序和涨幅分页查询
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public R<PageResult<StockUpdownDomain>> stockPage(Integer page, Integer pageSize) {
        // 设置分页参数
        PageHelper.startPage(page, pageSize);

        // 查询沪深两市个股行情列表查询
        List<StockUpdownDomain> list = stockRtInfoMapper.selectAll();

        // 将数据PageInfo下
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(list);

        // 将pageInfo封装到分页结果PageResult中
        PageResult<StockUpdownDomain> pageResult = new PageResult<>(pageInfo);
        return R.ok(pageResult);
    }

    /**
     * 功能描述：沪深两市涨跌停分时行情数据查询，查询T日每分钟的涨跌停数据（T：当前股票交易日）
     * 		查询每分钟的涨停和跌停的数据的同级；
     * 		如果不在股票的交易日内，那么就统计最近的股票交易下的数据
     * 	 map:
     * 	    upList:涨停数据统计
     * 	    downList:跌停数据统计
     * @return
     */
    @Override
    public R<Map> upDownCount() {
        // 获取当前最新股票交易日的开始时间和结束时间
        // 根据当前时间获取最新股票交易时间
        DateTime time = DateTimeUtil.getLastDate4Stock(DateTime.now());

        // 根据最新股票交易时间获取开始和结束时间
        Date openTime = DateTimeUtil.getOpenDate(time).toDate();
        Date closeTime = DateTimeUtil.getCloseDate(time).toDate();

        // TODO moke 测试数据
        openTime = DateTime.parse("2021-12-19 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        closeTime = DateTime.parse("2021-12-19 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();


        // 查询涨停数据
        List<Map> upList =  stockRtInfoMapper.getStockUpDownCount(openTime, closeTime, 1);
        // 查询跌停数据
        List<Map> downList = stockRtInfoMapper.getStockUpDownCount(openTime, closeTime, 0);

        // 封装数据
        Map<String, List> mapResult = new HashMap<>();
        mapResult.put("upList", upList);
        mapResult.put("downList", downList);

        // 返回数据
        return R.ok(mapResult);
    }

    /**
     * 将指定页的股票数据导出到excel表下
     * @param response
     * @param page  当前页
     * @param pageSize 每页大小
     */
    @Override
    public void stockExport(HttpServletResponse response, Integer page, Integer pageSize) {
        try {
            //1.设置响应数据的类型:excel
            response.setContentType("application/vnd.ms-excel");
            //2.设置响应数据的编码格式
            response.setCharacterEncoding("utf-8");
            //3.设置默认的文件名称
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = URLEncoder.encode("stockRt", "UTF-8");
            //设置默认文件名称
            response.setHeader("content-disposition", "attachment;filename=" + fileName + ".xlsx");
            // 4. 设置分页参数
            PageHelper.startPage(page, pageSize);
            // 5. 查询指定的股票数据
            List<StockUpdownDomain> stockList = stockRtInfoMapper.selectAll();
            if (CollectionUtils.isEmpty(stockList)) {
                R<Object> errorResult = R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
                // 因为是使用Servlet原生的Response返回数据，所以将错误信息转为Json返回
                Gson gson = new Gson();
                String jsonResult = gson.toJson(errorResult);
                // 返回
                response.getWriter().write(jsonResult);
                return;
            }
            // 6. 将List<StockUpDownDomain> 转换为 List<StockExcelDomain>
            List<StockExcelDomain> collect = stockList.stream().map(item -> {
                StockExcelDomain excelDomain = new StockExcelDomain();
                BeanUtils.copyProperties(item, excelDomain);
                return excelDomain;
            }).collect(Collectors.toList());

            // 7. Excel导出操作
            EasyExcel.write(response.getOutputStream(), StockExcelDomain.class)
                    .sheet("stockInfo")
                    .doWrite(collect);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 统计国内A股大盘T日和T-1日成交量对比
     * @return
     */
    @Override
    public R<Map> stockTradeVol4InnerMarket() {
        // 1. 获取国内大盘T日和恶T-1日的开盘时间和结束时间
        // 1.1 获取最近有效交易时间 T日
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        DateTime openDateTime = DateTimeUtil.getOpenDate(lastDateTime);
        // 转化为Date
        Date startTime = openDateTime.toDate();
        Date endTime = lastDateTime.toDate();
        // TODO moke 测试数据
        startTime = DateTime.parse("2022-01-03 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime = DateTime.parse("2022-01-03 14:20:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        // 1.2 获取最近有效交易日 T-日
        DateTime preLastDateTime = DateTimeUtil.getPreviousTradingDay(lastDateTime);
        DateTime preOpenDateTime = DateTimeUtil.getOpenDate(preLastDateTime);
        // 转换为Date
        Date preStartTime = preOpenDateTime.toDate();
        Date preEndTime = preLastDateTime.toDate();
        // TODO moke 测试数据
        preStartTime = DateTime.parse("2022-01-02 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        preEndTime = DateTime.parse("2022-01-02 14:20:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 获取上证和深证的大盘id
        List<String> marketIds = stockInfoConfig.getInner();

        // 3. 查询T日的成交量
        List<Map> volList =  stockMarketIndexInfoMapper.getStockTradeVol(marketIds, startTime, endTime);

        // 4. 查询T-1日的成交量
        List<Map> yesVolList =  stockMarketIndexInfoMapper.getStockTradeVol(marketIds, preStartTime, preEndTime);

        // 5. 封装结果
        HashMap<String, List> mapResult = new HashMap<>();
        mapResult.put("volList", volList);
        mapResult.put("yesVolList", yesVolList);

        // 6. 返回数据
        return R.ok(mapResult);
    }

    /**
     * 查询当前时间下股票的涨跌幅度区间统计功能
     * 如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询点
     * @return
     */
    @Override
    public R<Map> stockUpDownScopeCount() {
        // 1. 获取最新股票有效交易时间
        DateTime dateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastDate = dateTime.toDate();
        // TODO moke 测试数据
        lastDate = DateTime.parse("2021-12-30 09:32:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 根据指定的时间查询涨幅区间
        List<Map> infos = stockRtInfoMapper.stockUpDownScopeCount(lastDate);
        // 判断集合是否为空
        if(CollectionUtils.isEmpty(infos)) {
            infos = new ArrayList<>();
        }

        // 将查询出来的区间数据排序，并将没有查询出的区间数据count初始为0
        List<String> upDownRangeList = stockInfoConfig.getUpDownRange();
        List<Map> finalInfos = infos;
        List<Map> collect = upDownRangeList.stream().map(item -> {
            Optional<Map> title = finalInfos.stream().filter(info -> info.get("title").equals(item)).findFirst();
            // 判断结果是否有map
            Map map = null;
            if (title.isPresent()) {
                map = title.get();
            } else {
                map = new HashMap<>();
                map.put("title", item);
                map.put("count", 0);
            }

            return map;
        }).collect(Collectors.toList());

        // 封装结果数据
        String time = dateTime.toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        HashMap<String, Object> mapResult = new HashMap<>();
        mapResult.put("time", time);
        mapResult.put("infos", collect);

        return R.ok(mapResult);
    }

    /**
     * 功能描述：查询单个个股的分时行情数据，也就是统计指定股票T日每分钟的交易数据；
     *         如果当前日期不在有效时间内，则以最近的一个股票交易时间作为查询时间点
     * @param code 股票编码
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> stockScreenTimeSharing(String code) {
        // 1. 获取最新交易时间，和交易开始时间
        // 最新交易时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = lastDateTime.toDate();
        // 开始时间
        DateTime openDateTime = DateTimeUtil.getOpenDate(lastDateTime);
        Date startTime = openDateTime.toDate();

        // TODO moke 测试数据
        startTime = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 根据股票编码 开始时间 和当前时间 查询单个个股的分时行情数据
        List<Stock4MinuteDomain> stockInfoList = stockRtInfoMapper.getStockInfoCodeOrDate(code, startTime, endTime);

        // 3. 响应数据
        return R.ok(stockInfoList);
    }

    /**
     * 单个个股日K 数据查询 ，可以根据时间区间查询数日的K线数据
     *
     * @param stockCode 股票编码
     */
    @Override
    public R<List<Stock4EvrDayDomain>> stockCreenDkLine(String stockCode) {
        // 1. 获取查询的日期范围额
        // 获取结束时间
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = lastDateTime.toDate();
        // 获取开始时间
        DateTime openDateTime = lastDateTime.minusDays(10);
        Date startTime = openDateTime.toDate();

        // TODO moke 测试数据
        startTime = DateTime.parse("2022-01-01 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime = DateTime.parse("2022-01-07 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 2. 根据日期范围和 股票编码查询数据库
        List<Stock4EvrDayDomain> stock4EvrDayInfoList = stockRtInfoMapper.getStockInfo4EvrDay(stockCode, startTime, endTime);
        if (CollectionUtils.isEmpty(stock4EvrDayInfoList)) {
            stock4EvrDayInfoList = new ArrayList<>();
        }

        // 3. 封装数据返回
        return R.ok(stock4EvrDayInfoList);
    }

    /**
     * 外盘指数行情数据查询，根据时间和大盘点数降序排序取前4
     */
    @Override
    public R<List<Map<String, Object>>> innerExternalInfo() {
        // 大盘id集合
        List<String> innerIds = stockInfoConfig.getInner();

        // 获取最新有效的股票交易日
        DateTime lastDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date lastTime = lastDateTime.toDate();
        // TODO moke 测试数据
        lastTime = DateTime.parse("20211226120000", DateTimeFormat.forPattern("yyyyMMddHHmmss")).toDate();

        // 根据指定日期和大盘id查询数据
        List<Map<String, Object>> marketIndexInfoList = stockMarketIndexInfoMapper.getInnerExternalInfo(innerIds, lastTime);
        // 返回数据
        return R.ok(marketIndexInfoList.subList(0,4));
    }

    /**
     * 根据输入的个股代码，进行模糊查询，返回证券代码和证券名称
     * @param searchStr
     * @return
     */
    @Override
    public R<List<Map<String, Object>>> fuzzyQuery(String searchStr) {
        // 检查参数
        if(StringUtils.isBlank(searchStr)) {
            R.error(ResponseCode.DATA_ERROR.getMessage());
        }

        // 对参数进行模糊处理
        String searchStrFuzzy = "%" + searchStr + "%";

        // 根据股票编码模糊查询
        List<Map<String, Object>> stockRtInfoList = stockRtInfoMapper.getByCodeFuzzy(searchStrFuzzy);

        return R.ok(stockRtInfoList);
    }

    /**
     * 个股主营业务查询接口
     * @param code
     * @return
     */
    @Override
    public R<Map<String, Object>> getStockDescribe(String code) {
        if(StringUtils.isBlank(code)) {
            R.error(ResponseCode.DATA_ERROR.getMessage());
        }

        // 根据参数查询个股主营业务
        Map<String, Object> mapResult = stockBusinessMapper.getBySecCodeInfo(code);

        return R.ok(mapResult);
    }

    /**
     * 功能描述：统计每周内的股票数据信息，信息包含：
     * 股票ID、 一周内最高价、 一周内最低价 、周1开盘价、周5的收盘价、
     * 整周均价、以及一周内最大交易日期（一般是周五所对应日期）
     *
     * @param code
     * @return
     */
    @Override
    public R<List<Map<String, Object>>> getStockInfo(String code) {
        // 获取日期范围
        DateTime lastDate4Stock = DateTimeUtil.getLastDate4Stock(DateTime.now());
        Date endTime = lastDate4Stock.toDate();
        Date startTime = lastDate4Stock.minusDays(4).toDate();

        // TODO moke 测试数据
        startTime = DateTime.parse("2021-12-25 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();


        // 根据日期范围统计每周的股票数据信息
        List<Map<String, Object>> mapResult =  stockRtInfoMapper.getStockInfo4Week(code, startTime, endTime);

        return R.ok(mapResult);
    }

    /**
     * 获取个股最新分时行情数据，主要包含：
     * 	开盘价、前收盘价、最新价、最高价、最低价、成交金额和成交量、交易时间信息
     * @param code
     * @return
     */
    @Override
    public R<Map<String, Object>> stockScreenTimeSharingInfo(String code) {
        // 获取最新股票有效交易日
        DateTime lastDate4Stock = DateTimeUtil.getLastDate4Stock(DateTime.now());
        DateTime openDate = DateTimeUtil.getOpenDate(lastDate4Stock);

        Date endTime = lastDate4Stock.toDate();
        Date startTime = openDate.toDate();

        // TODO moke 测试数据
        startTime = DateTime.parse("2021-12-30 09:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        endTime = DateTime.parse("2021-12-30 15:00:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();

        // 根据股票开盘和关盘时间分时查询个股数据
        Map<String, Object> mapResult = stockRtInfoMapper.getStockScreenTimeInfo(code, startTime, endTime);

        return R.ok(mapResult);
    }

    /**
     * 个股交易流水行情数据查询--查询最新交易流水，按照交易时间降序取前10
     * @param code
     * @return
     */
    @Override
    public R<List<Map<String, Object>>> getStockTradinStatement(String code) {
        if(StringUtils.isBlank(code)) {
            return R.error(ResponseCode.DATA_ERROR.getMessage());
        }

        List<Map<String, Object>> mapResult = stockRtInfoMapper.getByCodeInfo(code);

        return R.ok(mapResult);
    }
}    